import { BaseService } from '@/core/base.service';
import { UserAllowBatchDto, UserInviteeQueryDto, UserQueryDto } from '@/dtos';
import { Injectable } from '@nestjs/common';
import { Prisma, User } from '@prisma/client';
import dayjs from 'dayjs';
import { OrderStatusEnum, OrderTypeEnum } from '@/enums';
import { ok } from '@/utils';

@Injectable()
export class UserService extends BaseService {
  async getInvitee({ type = 1, userId, params, sort }: UserInviteeQueryDto) {
    const { current = 1, pageSize = 20, ...where } = params;

    let sql = `
      SELECT #{fields} FROM User AS t
      WHERE t.isDeleted = false
        AND t.bindCode = (
          SELECT u.code FROM User AS u WHERE u.id = ${userId}
        )
        ${type === 2 ? `AND t.isReal = true` : ''}
    `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<User[]>(
      `${sql.replace(
        '#{fields}',
        `
          t.id, t.mobile, t.avatar, t.isReal, t.createdAt
        `,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`)
              .join(',')
          : 't.createdAt DESC'
      }
      LIMIT ${pageSize} 
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    const [{ count: allUserCount }] = await this.prisma.$queryRawUnsafe<any>(`
          SELECT COUNT(t.id) AS count FROM User AS t WHERE t.isDeleted = false AND t.bindCode = (
            SELECT u.code FROM User AS u WHERE u.id = ${userId}
          )
        `);
    // 实名人数统计
    // const [{ count: effectiveUserCount }] = await this.prisma
    //   .$queryRawUnsafe<any>(`
    //       SELECT COUNT(t.id) AS count FROM User AS t WHERE t.isDeleted = false AND t.bindCode = (
    //         SELECT u.code FROM User AS u WHERE u.id = ${userId}
    //       )
    //       AND t.isReal = true
    //     `);

    // const [{ count: effectiveUserCount }] = await this.prisma
    const inviteData = await this.prisma.$queryRawUnsafe<any>(`
          SELECT 
            COUNT(o.ownerId) AS count 
          FROM \`Order\` AS o
          LEFT JOIN 
            User AS u on o.ownerId = u.id
          WHERE 
            u.isDeleted = false 
            AND u.bindCode = (SELECT u.code FROM User AS u WHERE u.id = ${userId})
            AND u.isReal = true
            AND o.status = ${OrderStatusEnum.COMPLETED}
            AND o.type = ${OrderTypeEnum.MARKET}
            AND CONVERT_TZ(u.createdAt, 'UTC', 'Asia/Shanghai') >= '${dayjs('2024-10-17 14:00:00').format('YYYY-MM-DD HH:mm:ss')}'
            AND o.assetId not in (123, 124)
          GROUP BY o.ownerId
        `);

    const effectiveUserCount = inviteData.length;

    const isReal = [];
    for (const item of list) {
      const res = await this.prisma.$queryRawUnsafe<[{ count: number }]>(`
          SELECT 
            COUNT(o.ownerId) AS count 
          FROM \`Order\` AS o
          LEFT JOIN 
            User AS u on o.ownerId = u.id
          WHERE 
            u.isDeleted = false 
            AND o.ownerId = ${item.id}
            AND u.isReal = true
            AND o.status = ${OrderStatusEnum.COMPLETED}
            AND o.type = ${OrderTypeEnum.MARKET}
            AND CONVERT_TZ(u.createdAt, 'UTC', 'Asia/Shanghai') >= '${dayjs('2024-10-17 14:00:00').format('YYYY-MM-DD HH:mm:ss')}'
            AND o.assetId not in (123, 124)
          GROUP BY o.ownerId
        `);

      // console.log(res);
      // console.log(res.length);
      isReal[item.id] = res?.length > 0 ? 1 : 0;
    }

    return {
      meta: {
        allUserCount: Number(allUserCount ?? 0),
        effectiveUserCount: Number(effectiveUserCount ?? 0),
      },
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list?.map((item) => ({
        ...item,
        isReal: isReal[item.id],
      })),
    };
  }

  async query({ params, sort }: UserQueryDto) {
    const {
      current = 1,
      pageSize = 20,
      keyword,
      dateTimeRange,
      ...where
    } = params;

    let sql = `
      SELECT #{fields} FROM User AS t
      WHERE t.isDeleted = false
        ${!!where.mobile ? `AND t.mobile LIKE '%${where.mobile}%'` : ''}
        ${!!where.nickname ? `AND t.nickname LIKE '%${where.nickname}%'` : ''}
        ${!!where.realname ? `AND t.realname LIKE '%${where.realname}%'` : ''}
        ${!!where.code ? `AND t.code LIKE '%${where.code}%'` : ''}
        ${!!where.bindCode ? `AND t.bindCode LIKE '%${where.bindCode}%'` : ''}
        ${
          !!dateTimeRange && dateTimeRange.length >= 2
            ? `AND CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai') >= '${dayjs(
                dateTimeRange[0],
              ).format('YYYY-MM-DD HH:mm:ss')}' 
               AND CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai') <= '${dayjs(
                 dateTimeRange[1],
               ).format('YYYY-MM-DD HH:mm:ss')}'`
            : ''
        }
    `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(t.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<User[]>(
      `${sql.replace(
        '#{fields}',
        `
          t.id, t.mobile, t.realname, t.nickname, t.balance,
          t.avatar, t.isReal, t.isBlockd, isBatch, t.code, t.bindCode, 
          t.frozen, t.idCard, t.userCustId, t.createdAt,
          (
            SELECT COUNT(c.id) 
            FROM Token AS c 
            WHERE c.ownerId = t.id
              AND c.isDeleted = false
          ) AS hold
        `,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => {
                if (k === 'hold') {
                  return `${k} ${v === 'descend' ? 'DESC' : 'ASC'}`;
                }
                return `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`;
              })
              .join(',')
          : 't.createdAt DESC'
      }
      LIMIT ${pageSize} 
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((u: any) => ({
        ...u,
        balance: Number(u.balance),
        frozen: Number(u.frozen),
        hold: Number(u.hold),
      })),
    };
  }

  async allowBatch({ isBatch, rows }: UserAllowBatchDto) {
    for (let user of rows) {
      await this.prisma.user.update({
        where: {
          mobile: user.mobile,
        },
        data: {
          isBatch,
        },
      });
    }
    return true;
  }

  async findOne(
    where: Prisma.UserWhereUniqueInput,
    select?: Prisma.UserSelect,
  ): Promise<User | null> {
    return this.prisma.user.findUnique({
      where,
      select,
    });
  }

  async create(data: Prisma.UserCreateInput): Promise<User> {
    return this.prisma.user.create({
      data,
    });
  }

  async update(params: {
    where: Prisma.UserWhereUniqueInput;
    data: Prisma.UserUpdateInput;
  }): Promise<User> {
    const { where, data } = params;
    return this.prisma.user.update({
      where,
      data,
    });
  }

  async remove(where: Prisma.UserWhereUniqueInput): Promise<User> {
    return this.prisma.user.delete({
      where,
    });
  }

  async destroyRanking(id) {
    const list = await this.prisma.user.findMany({
      where: {
        destroyPoints: {
          gt: 0, // >
        },
      },
      select: {
        nickname: true,
        destroyPoints: true,
      },
      take: 100,
      orderBy: {
        destroyPoints: 'desc',
      },
    });

    const point = await this.prisma.user.findUnique({
      where: {
        id,
      },
      select: {
        destroyPoints: true,
      },
    });

    return {
      list,
      point: point,
    };
  }
}
